package com.example.yilanchatserver.common;

import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;


public class ConvertUtils {


    public static <T> T convert(Object source, Class<T> targetClass) {
        if (source == null) {
            return null;
        }
        try {
            T target = targetClass.newInstance();
            BeanUtils.copyProperties(source, target);
            return target;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static <T> List<T> convert(List source, Class<T> clazz) {
        ArrayList<T> result = new ArrayList<>();
        if (CollectionUtils.isEmpty(source)) {
            return result;
        }
        for (Object s : source) {
            result.add(convert(s, clazz));
        }
        return result;
    }

    /**
     * Object转map
     *
     * @param object
     * @return
     * @throws IllegalAccessException
     */
    public static Map<String, String> objectToMap(Object object) throws IllegalAccessException {
        Map<String, String> map = new HashMap<>();
        Class<?> clazz = object.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            String fieldName = field.getName();
            Object value = field.get(object);
            if (null == value) {
                map.put(fieldName, "");
            } else {
                map.put(fieldName, value.toString());
            }
        }
        return map;
    }

    /**
     * Object转map
     *
     * @param object
     * @return
     * @throws IllegalAccessException
     */
    public static Map<String, Object> objectToObjectMap(Object object) throws IllegalAccessException {
        Map<String, Object> map = new HashMap<>();
        Class<?> clazz = object.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            String fieldName = field.getName();
            Object value = field.get(object);
            if (null == value) {
                map.put(fieldName, "");
            } else {
                map.put(fieldName, value);
            }
        }
        return map;
    }

    /**
     * 泛型复制
     *
     * @param source
     * @param targetClass
     * @param <T>
     * @return
     */
    public static <T> T CopyT(Object source, Class<T> targetClass) {
        T target = null;
        try {
            target = targetClass.newInstance();
            Class<?> classType = source.getClass();
            Field[] flds = classType.getDeclaredFields();
            for (Field fld : flds) {
                fld.setAccessible(true);
                Object value = fld.get(source);
                Field targetFld = target.getClass().getDeclaredField(fld.getName());
                targetFld.setAccessible(true);
                targetFld.set(target, value);
            }
        } catch (InstantiationException e1) {
            e1.printStackTrace();
        } catch (IllegalAccessException e1) {
            e1.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        return target;
    }

    /**
     * 泛型复制
     *
     * @param source
     * @param targetClass
     * @param <T>
     * @return
     */
    public static <T> List<T> CopyT(List source, Class<T> targetClass) {
        ArrayList<T> result = new ArrayList<>();
        if (CollectionUtils.isEmpty(source)) {
            return result;
        }
        for (Object s : source) {
            result.add(CopyT(s, targetClass));
        }
        return result;
    }

    /**
     * 将对象按照key分组转换，然后将结果存入map中
     * <p>
     * 其中map的key通过keyFun.apply(obj)得到的
     * map的value为分组转换后的List容器
     * <p>
     * 如果仅需要对象根据key分组，请直接使用Stream.collect(Collectors.groupingBy(keyFun))
     *
     * @param objs   要进行处理的对象
     * @param keyFun key获取方法，不可为空
     * @param mapFun 转换方法，不可为空
     * @param <K>    key类型
     * @param <N>    转换后的对象的类型
     * @param <O>    要处理的对象的类型
     * @return 返回分组转换后的结果
     */
    public static <K, N, O> Map<K, List<N>> groupByFieldAndMap(final Collection<O> objs,
                                                               final Function<? super O, ? extends K> keyFun,
                                                               final Function<? super O, ? extends N> mapFun) {
        return groupByFieldAndMap(objs, keyFun, mapFun, null);
    }

    /**
     * 将对象按照key分组转换，然后将结果存入map中
     * <p>
     * 其中map的key通过keyFun.apply(obj)得到的
     * map的value为分组转换后的List容器
     * <p>
     * 如果仅需要对象根据key分组，请直接使用Stream.collect(Collectors.groupingBy(keyFun))
     *
     * @param objs   要进行处理的对象
     * @param keyFun key获取方法，不可为空
     * @param mapFun 转换方法，不可为空
     * @param filter 过滤方法，可为空
     * @param <K>    key类型
     * @param <N>    转换后的对象的类型
     * @param <O>    要处理的对象的类型
     * @return 返回分组转换后的结果
     */
    public static <K, N, O> Map<K, List<N>> groupByFieldAndMap(final Collection<O> objs,
                                                               final Function<? super O, ? extends K> keyFun,
                                                               final Function<? super O, ? extends N> mapFun,
                                                               final Predicate<? super O> filter) {
        Objects.requireNonNull(keyFun);
        Objects.requireNonNull(mapFun);
        if (null == objs || 0 == objs.size()) {
            return new HashMap<>(0);
        }

        Map<K, List<N>> resultMap = new HashMap<>();
        objs.stream().forEach(obj -> convertToMap(resultMap, obj, keyFun, mapFun, filter));

        return resultMap;
    }

    /**
     * Take each object in map.
     * The way that get the key of object is `keyFun`.
     *
     * @param objs   The objects.
     * @param keyFun The function that is used to get the key of object.
     *               Function will return a blank HashMap if the `keyFun` is null.
     * @param <K>    Key type.
     * @param <O>    Object type.
     * @return Return the map that after handle.
     */
    public static <K, O> Map<K, O> groupSingleByKey(final Collection<O> objs, final Function<? super O, ? extends K> keyFun) {
        if (null == objs || 0 == objs.size() || null == keyFun) {
            return new HashMap<>(0);
        }

        Map<K, O> resultMap = new HashMap<>();
        for (O obj : objs) {
            if (null == obj) {
                continue;
            }
            resultMap.put(keyFun.apply(obj), obj);
        }
        return resultMap;
    }

    /**
     * Take each object in map.
     * The way that get the key of object is `keyFun`.
     *
     * @param objs   The objects.
     * @param keyFun The function that is used to get the key of object.
     *               Function will return a blank HashMap if the `keyFun` is null.
     * @param filter The filter function.
     * @param <K>    Key type.
     * @param <O>    Object type.
     * @return Return the map that after handle.
     */
    public static <K, O> Map<K, O> groupSingleByKey(final Collection<O> objs,
                                                    final Function<? super O, ? extends K> keyFun,
                                                    final Predicate<? super O> filter) {
        if (null == objs || 0 == objs.size() || null == keyFun) {
            return new HashMap<>(0);
        }

        Map<K, O> resultMap = new HashMap<>();
        for (O obj : objs) {
            if (null == obj) {
                continue;
            }
            if (null != filter && !filter.test(obj)) {
                continue;
            }

            resultMap.put(keyFun.apply(obj), obj);
        }
        return resultMap;
    }

    /**
     * Take each object in map.
     * The way that get the key of object is `keyFun`.
     *
     * @param objs   The objects.
     * @param keyFun The function that is used to get the key of object.
     *               Function will return a blank HashMap if the `keyFun` is null.
     * @param map    Th
     * @param filter The filter function.
     * @param <K>    Key type.
     * @param <O>    Object type.
     * @return Return the map that after handle.
     */
    public static <K, O, V> Map<K, V> groupSingleByKey(final Collection<O> objs,
                                                       final Function<? super O, ? extends K> keyFun,
                                                       final Function<? super O, ? extends V> map,
                                                       final Predicate<? super O> filter) {
        if (null == objs || 0 == objs.size() || null == keyFun) {
            return new HashMap<>(0);
        }

        Map<K, V> resultMap = new HashMap<>();
        for (O obj : objs) {
            if (null == obj) {
                continue;
            }
            if (null != filter && !filter.test(obj)) {
                continue;
            }

            resultMap.put(keyFun.apply(obj), map.apply(obj));
        }
        return resultMap;
    }


    public static <K, O> Collection<O> distinctAndFilter(final Collection<O> objs,
                                                         final Function<? super O, ? extends K> keyFun,
                                                         final Predicate<? super O> filter) {
        Map<K, O> resultMap = new HashMap<>();
        for (O obj : objs) {
            if (null == obj || (null != filter && !filter.test(obj))) {
                continue;
            }
            resultMap.put(keyFun.apply(obj), obj);
        }

        if (0 == resultMap.size()) {
            return new ArrayList<>(0);
        }

        return resultMap.values();
    }

    /**
     * 将对象按照指定方法转换并按照key，存入指定map存放的容器中
     *
     * @param map    存放容器的Map
     * @param obj    要进行操作的对象
     * @param keyFun 获取Key的方法
     * @param mapFun 对象转换方法
     * @param filter 过滤方法，可为空
     * @param <K>    Key类型
     * @param <N>    转换后对象类型
     * @param <O>    对象类型
     */
    private static <K, N, O> void convertToMap(final Map<K, List<N>> map, final O obj,
                                               final Function<? super O, ? extends K> keyFun,
                                               final Function<? super O, ? extends N> mapFun,
                                               final Predicate<? super O> filter) {
        if (null == obj) {
            return;
        }
        if (null != filter && !filter.test(obj)) {
            return;
        }

        K key = keyFun.apply(obj);
        if (null == key) {
            return;
        }

        List<N> tempList = map.computeIfAbsent(key, k -> new ArrayList<>());
        tempList.add(mapFun.apply(obj));
    }

}